package org.example.weboj.service.impl;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.nio.charset.StandardCharsets;

import com.github.dockerjava.api.command.InspectContainerResponse;
import com.github.dockerjava.api.model.*;
import org.example.weboj.dto.CodeExecutionRequest;
import org.example.weboj.dto.CodeExecutionResult;
import org.example.weboj.service.CodeExecutionService;
import org.springframework.stereotype.Service;

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.async.ResultCallback;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientConfig;
import com.github.dockerjava.core.DockerClientImpl;
import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
import com.github.dockerjava.transport.DockerHttpClient;


@Service
public class CodeExecutionServiceImpl implements CodeExecutionService {

    private final DockerClient dockerClient;

    public CodeExecutionServiceImpl() {
        DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder().build();

        DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
            .dockerHost(config.getDockerHost())
            .sslConfig(config.getSSLConfig())
            .build();

        this.dockerClient = DockerClientImpl.getInstance(config, httpClient);
    }

    @Override
    public CodeExecutionResult executeCode(CodeExecutionRequest request) {
        String containerId = null;
        Path codeDir = null;
        File codeFile = null;

        try {
            System.out.println("Starting code execution for language: " + request.getLanguage());

            // 创建临时目录和代码文件
            codeDir = createTempDirectory();
            codeFile = createCodeFile(codeDir, request);

            // 创建并启动Docker容器
            containerId = createAndStartContainer(request, codeDir, codeFile);

            // 等待容器执行完成
            boolean timedOut = waitForContainerCompletion(containerId, request.getTimeLimit());
            if (timedOut) {
                return handleTimeout(containerId, codeFile, codeDir, request.getTimeLimit());
            }

            // 获取容器输出
            String output = getContainerOutput(containerId);

            // 检查容器退出码
            Long exitCode = null;
            try {
                InspectContainerResponse containerInfo = dockerClient.inspectContainerCmd(containerId).exec();
                exitCode = Long.valueOf(containerInfo.getState().getExitCode());
                System.out.println("Container exit code: " + exitCode);
            } catch (Exception e) {
                System.err.println("Error inspecting container: " + e.getMessage());
            }

            // 清理资源
            cleanupResources(containerId, codeFile, codeDir);

            // 解析执行结果
            return parseExecutionResult(output, exitCode, request);

        } catch (Exception e) {
            // 确保资源被清理
            cleanupResources(containerId, codeFile, codeDir);

            CodeExecutionResult result = new CodeExecutionResult();
            result.setError(e.getMessage());
            result.setStatus("RUNTIME_ERROR");
            return result;
        }
    }

    /**
     * 创建临时目录
     */
    private Path createTempDirectory() throws IOException {
        String tempDir = System.getProperty("java.io.tmpdir");
        String uniqueId = UUID.randomUUID().toString();
        Path codeDir = Paths.get(tempDir, "code", uniqueId);
        Files.createDirectories(codeDir);
        System.out.println("Created temp directory: " + codeDir);
        return codeDir;
    }

    /**
     * 创建代码文件
     */
    private File createCodeFile(Path codeDir, CodeExecutionRequest request) throws IOException {
        String fileName = getFileName(request.getLanguage());
        File codeFile = new File(codeDir.toFile(), fileName);
        try (FileWriter writer = new FileWriter(codeFile, StandardCharsets.UTF_8)) {
            // 对于Python文件，添加编码声明
            if (request.getLanguage().equalsIgnoreCase("python")) {
                writer.write("# -*- coding: utf-8 -*-\n");
            }
            writer.write(request.getCode());
        }
        System.out.println("Created code file: " + codeFile.getAbsolutePath());
        return codeFile;
    }

    /**
     * 创建并启动Docker容器
     */
    private String createAndStartContainer(CodeExecutionRequest request, Path codeDir, File codeFile) {
        String imageName = getDockerImage(request.getLanguage());
        System.out.println("Using Docker image: " + imageName);

        // 如果有输入数据，创建一个输入文件
        File inputFile = null;
        if (request.getInput() != null && !request.getInput().isEmpty()) {
            try {
                inputFile = new File(codeDir.toFile(), "input.txt");
                try (FileWriter writer = new FileWriter(inputFile, StandardCharsets.UTF_8)) {
                    writer.write(request.getInput());
                }
                // 确保文件有正确的权限
                inputFile.setReadable(true, false);
                inputFile.setWritable(true, false);
                inputFile.setExecutable(true, false);
                System.out.println("Created input file: " + inputFile.getAbsolutePath());
            } catch (IOException e) {
                System.err.println("Error creating input file: " + e.getMessage());
            }
        }

        String[] cmd = getExecutionCommand(request.getLanguage(), codeFile.getName());
        System.out.println("Execution command: " + String.join(" ", cmd));

        try {
            HostConfig hostConfig = HostConfig.newHostConfig()
                    .withBinds(new Bind(codeDir.toString(), new Volume("/code")))
                    .withMemory(request.getMemoryLimit() * 1024L * 1024L)  // 单位：字节
                    .withMemorySwap(request.getMemoryLimit() * 1024L * 1024L) // 禁用 swap
                    .withCpuPeriod(100_000L)
                    .withCpuShares(request.getTimeLimit() * 10)
                    .withNetworkMode("none"); // 禁用网络

            CreateContainerResponse container = dockerClient.createContainerCmd(imageName)
                    .withHostConfig(hostConfig)
                    .withCmd(cmd)
                    .exec();

            String containerId = container.getId();
            System.out.println("Created container with ID: " + containerId);
            dockerClient.startContainerCmd(containerId).exec();
            System.out.println("Started container with memory limit: " + request.getMemoryLimit() + "MB and time limit: " + request.getTimeLimit() + "s");
            return containerId;
        } catch (Exception e) {
            System.err.println("Error creating container: " + e.getMessage());
            e.printStackTrace();
            throw new RuntimeException("Failed to create container", e);
        }
    }


    /**
     * 等待容器执行完成
     * @return 如果超时返回true，否则返回false
     */
    private boolean waitForContainerCompletion(String containerId, int timeLimit) {
        ResultCallback.Adapter<WaitResponse> callback = new ResultCallback.Adapter<>();
        dockerClient.waitContainerCmd(containerId).exec(callback);
        try {
            boolean completed = callback.awaitCompletion(timeLimit + 2, TimeUnit.SECONDS);
            if (!completed) {
                System.out.println("Container execution timed out after " + (timeLimit + 2) + " seconds");
                return true;
            }
            System.out.println("Container execution completed");
            return false;
        } catch (Exception e) {
            System.err.println("Error waiting for container completion: " + e.getMessage());
            throw new RuntimeException("Failed to wait for container completion", e);
        }
    }

    /**
     * 处理超时情况
     */
    private CodeExecutionResult handleTimeout(String containerId, File codeFile, Path codeDir, int timeLimit) {
        // 超时处理
        CodeExecutionResult result = new CodeExecutionResult();
        result.setError("Execution timed out after " + timeLimit + " seconds");
        result.setStatus("TIMEOUT");
        result.setOutput("Time Limit Exceeded");

        // 强制终止容器
        try {
            dockerClient.killContainerCmd(containerId).exec();
        } catch (Exception e) {
            System.err.println("Error killing container: " + e.getMessage());
        }

        // 清理资源
        cleanupResources(containerId, codeFile, codeDir);

        return result;
    }

    /**
     * 获取容器输出
     */
    private String getContainerOutput(String containerId) {
        StringBuilder outputBuilder = new StringBuilder();
        ResultCallback.Adapter<Frame> logCallback = new ResultCallback.Adapter<>() {
            @Override
            public void onNext(Frame item) {
                try {
                    String frameContent = new String(item.getPayload()).trim();
                    System.out.println("Container output frame type: " + item.getStreamType() + ", content: " + frameContent);
                    outputBuilder.append(frameContent).append("\n");
                } catch (Exception e) {
                    System.err.println("Error processing frame: " + e.getMessage());
                }
            }

            @Override
            public void onError(Throwable throwable) {
                System.err.println("Error reading logs: " + throwable.getMessage());
                super.onError(throwable);
            }
        };

        System.out.println("Reading container logs");
        dockerClient.logContainerCmd(containerId)
                .withStdOut(true)
                .withStdErr(true)
                .withFollowStream(true)
                .withTailAll()
                .exec(logCallback);
        try {
            logCallback.awaitCompletion(10, TimeUnit.SECONDS);
        } catch (Exception e) {
            System.err.println("Error waiting for logs: " + e.getMessage());
        }

        String output = outputBuilder.toString().trim();
        System.out.println("Final output: " + output);
        return output;
    }

    /**
     * 清理资源
     */
    private void cleanupResources(String containerId, File codeFile, Path codeDir) {
        if (containerId != null) {
            try {
                dockerClient.removeContainerCmd(containerId).withForce(true).exec();
            } catch (Exception e) {
                System.err.println("Error removing container: " + e.getMessage());
            }
        }

        if (codeFile != null) {
            try {
                Files.deleteIfExists(codeFile.toPath());
            } catch (Exception e) {
                System.err.println("Error deleting code file: " + e.getMessage());
            }
        }

        if (codeDir != null) {
            try {
                deleteDirectory(codeDir.toFile());
            } catch (Exception e) {
                System.err.println("Error deleting directory: " + e.getMessage());
            }
        }
    }

    /**
     * 解析执行结果
     */
    private CodeExecutionResult parseExecutionResult(String output, Long exitCode, CodeExecutionRequest request) {
        CodeExecutionResult result = new CodeExecutionResult();
        result.setOutput(output.isEmpty() ? "No output" : output);

        // Check for memory limit exceeded (exit code 137)
        if (exitCode != null && exitCode == 137) {
            result.setStatus("MEMORY_LIMIT_EXCEEDED");
            result.setError("Memory limit exceeded. Allowed: " + request.getMemoryLimit() + "MB");
            return result;
        }

        // Check for memory limit exceeded patterns in output
        if (output.contains("Memory limit exceeded") || 
            output.contains("Killed") || 
            output.contains("Out of memory") ||
            output.contains("MemoryError") ||
            output.contains("Cannot allocate memory") ||
            output.contains("Memory allocation failed")) {
            result.setStatus("MEMORY_LIMIT_EXCEEDED");
            result.setError("Memory limit exceeded. Allowed: " + request.getMemoryLimit() + "MB");
            return result;
        }

        // Check for time limit exceeded
        if (output.contains("Time Limit Exceeded")) {
            result.setStatus("TIMEOUT");
            result.setError("Execution timed out after " + request.getTimeLimit() + " seconds");
            return result;
        }

        // 根据不同的编程语言处理错误
        String language = request.getLanguage().toLowerCase();
        switch (language) {
            case "python":
                if (isPythonCompilationError(output)) {
                    result.setStatus("COMPILATION_ERROR");
                    result.setError(output);
                    return result;
                } else if (isPythonRuntimeError(output)) {
                    result.setStatus("RUNTIME_ERROR");
                    result.setError(output);
                    return result;
                }
                break;

            case "java":
                if (isJavaCompilationError(output)) {
                    result.setStatus("COMPILATION_ERROR");
                    result.setError(output);
                    return result;
                } else if (isJavaRuntimeError(output)) {
                    result.setStatus("RUNTIME_ERROR");
                    result.setError(output);
                    return result;
                }
                break;

            case "c", "cpp":
                if (isCppCompilationError(output)) {
                    result.setStatus("COMPILATION_ERROR");
                    result.setError(output);
                    return result;
                } else if (isCppRuntimeError(output)) {
                    result.setStatus("RUNTIME_ERROR");
                    result.setError(output);
                    return result;
                }
                break;

            case "javascript":
                if (isJavaScriptCompilationError(output)) {
                    result.setStatus("COMPILATION_ERROR");
                    result.setError(output);
                    return result;
                } else if (isJavaScriptRuntimeError(output)) {
                    result.setStatus("RUNTIME_ERROR");
                    result.setError(output);
                    return result;
                }
                break;
        }

        // 如果有预期输出，则进行比较
        if (request.getExpectedOutput() != null && !request.getExpectedOutput().isEmpty()) {
            String actualOutput = output.trim();
            String expectedOutput = request.getExpectedOutput().trim();
            
            if (actualOutput.equals(expectedOutput)) {
                result.setStatus("SUCCESS");
                result.setError(null);
            } else {
                result.setStatus("WRONG_ANSWER");
                result.setError(String.format("Expected output: %s\nActual output: %s", 
                    expectedOutput, actualOutput));
            }
        } else {
            // 如果没有预期输出，则只要程序正常执行就认为是成功的
            result.setStatus("SUCCESS");
            result.setError(null);
        }

        return result;
    }

    /**
     * 判断Python编译错误
     */
    private boolean isPythonCompilationError(String output) {
        return output.contains("SyntaxError") ||
               output.contains("IndentationError") ||
               output.contains("NameError") ||
               output.contains("TypeError") ||
               output.contains("ImportError") ||
               output.contains("ModuleNotFoundError") ||
               output.contains("AttributeError") ||
               output.contains("ValueError") ||
               output.contains("EOF while parsing");
    }

    /**
     * 判断Python运行时错误
     */
    private boolean isPythonRuntimeError(String output) {
        return output.contains("ZeroDivisionError") ||
               output.contains("IndexError") ||
               output.contains("KeyError") ||
               output.contains("FileNotFoundError") ||
               output.contains("PermissionError") ||
               output.contains("TimeoutError") ||
               output.contains("RecursionError");
    }

    /**
     * 判断Java编译错误
     */
    private boolean isJavaCompilationError(String output) {
        return output.contains("error:") ||
               output.contains("cannot find symbol") ||
               output.contains("incompatible types") ||
               output.contains("';' expected") ||
               output.contains("class, interface, or enum expected") ||
               output.contains("illegal start of expression") ||
               output.contains("unclosed string literal") ||
               output.contains("unclosed character literal");
    }

    /**
     * 判断Java运行时错误
     */
    private boolean isJavaRuntimeError(String output) {
        return output.contains("Exception in thread") ||
               output.contains("java.lang.NullPointerException") ||
               output.contains("java.lang.ArrayIndexOutOfBoundsException") ||
               output.contains("java.lang.ClassCastException") ||
               output.contains("java.lang.ArithmeticException") ||
               output.contains("java.lang.StackOverflowError") ||
               output.contains("java.lang.OutOfMemoryError");
    }

    /**
     * 判断C/C++编译错误
     */
    private boolean isCppCompilationError(String output) {
        return output.contains("error:") ||
               output.contains("undefined reference") ||
               output.contains("expected") ||
               output.contains("invalid conversion") ||
               output.contains("no matching function") ||
               output.contains("expected ';'") ||
               output.contains("expected ')'") ||
               output.contains("expected '}'");
    }

    /**
     * 判断C/C++运行时错误
     */
    private boolean isCppRuntimeError(String output) {
        return output.contains("Segmentation fault") ||
               output.contains("Aborted") ||
               output.contains("Floating point exception") ||
               output.contains("double free") ||
               output.contains("stack overflow") ||
               output.contains("stack smashing detected");
    }

    /**
     * 判断JavaScript编译错误
     */
    private boolean isJavaScriptCompilationError(String output) {
        return output.contains("SyntaxError") ||
               output.contains("ReferenceError") ||
               output.contains("Unexpected token") ||
               output.contains("Missing") ||
               output.contains("Expected") ||
               output.contains("is not defined") ||
               output.contains("Unexpected end of input") ||
               output.contains("Invalid or unexpected token");
    }

    /**
     * 判断JavaScript运行时错误
     */
    private boolean isJavaScriptRuntimeError(String output) {
        return output.contains("TypeError") ||
               output.contains("RangeError") ||
               output.contains("URIError") ||
               output.contains("EvalError") ||
               output.contains("InternalError") ||
               output.contains("Cannot read properties") ||
               output.contains("Cannot set property") ||
               output.contains("Maximum call stack size exceeded") ||
               output.contains("Cannot read") ||
               output.contains("Cannot set") ||
               output.contains("is not a function") ||
               output.contains("is not a constructor") ||
               output.contains("is not iterable");
    }

    private String getFileName(String language) {
        return switch (language.toLowerCase()) {
            case "python" -> "main.py";
            case "c" -> "main.c";
            case "cpp" -> "main.cpp";
            case "java" -> "Main.java";
            case "javascript" -> "main.js";
            default -> throw new IllegalArgumentException("Unsupported language: " + language);
        };
    }

    private String getDockerImage(String language) {
        switch (language.toLowerCase()) {
            case "python": return "python:3.9";
            case "c", "cpp": return "gcc:latest";
            case "java": return "openjdk:11";
            case "javascript": return "node:latest";
            default: throw new IllegalArgumentException("Unsupported language: " + language);
        }
    }

    private String[] getExecutionCommand(String language, String fileName) {
        switch (language.toLowerCase()) {
            case "python": 
                return new String[]{"sh", "-c", "cd /code && cat input.txt | python " + fileName};
            case "c": 
                return new String[]{"sh", "-c", "cd /code && gcc " + fileName + " -o main && cat input.txt | ./main"};
            case "cpp": 
                return new String[]{"sh", "-c", "cd /code && g++ " + fileName + " -o main && cat input.txt | ./main"};
            case "java": 
                return new String[]{"sh", "-c", "cd /code && javac " + fileName + " && cat input.txt | java Main"};
            case "javascript": 
                return new String[]{"sh", "-c", "cd /code && cat input.txt | node " + fileName};
            default: 
                throw new IllegalArgumentException("Unsupported language: " + language);
        }
    }

    /**
     * 递归删除目录及其内容
     */
    private void deleteDirectory(File directory) {
        if (directory.exists()) {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        deleteDirectory(file);
                    } else {
                        file.delete();
                    }
                }
            }
            directory.delete();
        }
    }

    /**
     * 检查语言是否需要可写文件系统
     */
    private boolean needsWritableFilesystem(String language) {
        return switch (language.toLowerCase()) {
            case "c", "cpp", "java" -> true; // 这些语言需要编译，需要可写文件系统
            default -> false; // 其他语言不需要
        };
    }
}
